﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Reflection;

using SharedGenomics.Workbench.Core;
using SharedGenomics.Workbench.Messages;
using SharedGenomics.Workbench.Models;
using SharedGenomics.Workbench.PresentationModel;
using SharedGenomics.Workbench.Views;
using SharedGenomics.Workbench.Services;
using SharedGenomics.Workbench.Annotations;
using SharedGenomics.Domain;
using SharedGenomics.Annotations;

namespace SharedGenomics.Workbench.Presenters
{
    public class AnalysisResultsPresenter : Presenter
    {
        private bool _areAvailableJobsVisible;

        public AnalysisResultsPresenter(IAnalysisResultsView view, IAnalysisResultsModel model, IApplicationController appController, IDataColumnSchemaFactory schemaFactory, AnnotationSetContextMenuProvider contextMenuProvider, AnalysisResultsFilterPresenter jobsPresenter, IAnalysisResultsRepository resultsRepository)
            : base(view)
        {
            //register for view events
            view.ShowUcscBrowserClicked += new EventHandler(this.View_ShowUcscBrowserClicked);
            view.ShowPathwayFrequenciesClicked += new EventHandler(this.View_ShowPathwayFrequenciesClicked);
            view.ContextMenuRequired += new EventHandler<ContextMenuRequiredEventArgs>(this.View_ContextMenuRequired);

            //register for job presenter events
            jobsPresenter.CollapsedToggleRequested += (o, e) => { this.ToggleAvailableJobsVisible(); };
            jobsPresenter.JobResultsLoadRequested += JobsPresenter_JobResultsLoadRequested;

            //set model on view
            view.Model = model;

            //set private properties
            this.ResultsView = view;
            this.Model = model;
            this.ApplicationController = appController;
            this.SchemaFactory = schemaFactory;
            this.ContextMenuProvider = contextMenuProvider;
            this.JobsPresenter = jobsPresenter;
            this.ResultsRepository = resultsRepository;

            this._areAvailableJobsVisible = true;
        }

        private void JobsPresenter_JobResultsLoadRequested(object sender, JobResultsLoadEventArgs e)
        {
            try
            {
                //load analysis results for job
                this.ResultsRepository.GetResultsForJob(e.JobId).BindAction(results => 
                {
                    //get the column layout for the AnalysisResult type result set
                    this.CurrentColumnSchema = this.SchemaFactory.GetColumnSchemaForType(results.ResultType);

                    //clear the current column collection in the view and then add the columns for the new result type
                    this.ResultsView.ResultsGrid.Columns = this.CurrentColumnSchema;
                    this.Model.LoadResultSet(results, this.CurrentColumnSchema);

                    //notify listeners that a new job has been loaded
                    AnnotationResultsLoadedMessage loadMessage = new AnnotationResultsLoadedMessage(e.JobId);
                    this.EventBroker.Publish(loadMessage);

                    //hide the available results view
                    this.AreAvailableJobsVisible = false;
                });
            }
            catch (Exception ex)
            {
                //TODO: catch more specific exception type!
                this.View.DisplayError(String.Format("Unable to load results for job: {0}", ex.Message));
            }
        }

        private void View_ContextMenuRequired(object sender, ContextMenuRequiredEventArgs e)
        {
            //get property associated with the column - if it can be annotated, try to get the annotations for the value
            if(this.CurrentColumnSchema.IsSnpColumn(e.ColumnIndex))
            {
                AnalysisResult result = this.Model[e.RowIndex];
                PropertyInfo pi = result.GetType().GetProperty(this.CurrentColumnSchema[e.ColumnIndex].PropertyName);
                string snpName = pi.GetValue(result, null) as string;

                AnnotationInfoSet annotations;
                if (this.Model.TryGetAnnotationSetForValue(snpName, out annotations))
                {
                    e.ContextMenu = this.ContextMenuProvider.CreateContextMenu(annotations);
                }
            }
        }

        private void View_ShowPathwayFrequenciesClicked(object sender, EventArgs e)
        {
            var pathwayFrequencies = this.Model.GetPathwayFrequenciesForResults(this.ResultsView.SelectedResultIndices);
            this.ApplicationController.CreateAndShowPresenter<KeggPathwayFrequenciesPresenter>(FormDisplayMode.NonModalDialog, p => p.PathwayFrequencies = pathwayFrequencies);
        }

        private void View_ShowUcscBrowserClicked(object sender, EventArgs e)
        {
            IList<WiggleElement> wiggleElements = this.Model.GetSortedWiggleElementListForResults(this.ResultsView.SelectedResultIndices);
            this.ApplicationController.CreateAndShowPresenter<UcscBrowserPresenter>(FormDisplayMode.NonModalDialog, p => p.WiggleElements = wiggleElements);
        }

        private void ToggleAvailableJobsVisible()
        {
            this.AreAvailableJobsVisible = !this.AreAvailableJobsVisible;
        }

        private bool AreAvailableJobsVisible
        {
            get { return this._areAvailableJobsVisible; }
            set
            {
                bool valueChanging = this._areAvailableJobsVisible ^ value;
                if (valueChanging)
                {
                    //update view to show/hide the available jobs view
                    this.ResultsView.JobViewHeight = value ? 400 : this.JobsPresenter.MinimumViewHeight;

                    //notify the jobs presenter that it's shown/hidden state has changed
                    this.JobsPresenter.ToggleCollapsed();

                    this._areAvailableJobsVisible = value;
                }
            }
        }

        private IAnalysisResultsView ResultsView { get; set; }
        private IAnalysisResultsModel Model { get; set; }
        private IApplicationController ApplicationController { get; set; }
        private IDataColumnSchemaFactory SchemaFactory { get; set; }
        private AnnotationSetContextMenuProvider ContextMenuProvider { get; set; }
        private AnalysisResultDataColumnSchema CurrentColumnSchema { get; set; }
        private IAnalysisResultsRepository ResultsRepository { get; set; }

        [Region("Jobs")]
        public AnalysisResultsFilterPresenter JobsPresenter { get; private set; }
    }
}
